import { AFFIX_TEMPLATE, AFFIX_REGEXP } from "../frame.config";

export default class NativeClient {
	private static client: NativeClientIns;
	private static used_token: string[] = [];
	private static ccdata_cache: Map<string, any> = new Map();
	private static response_map: Map<string, mtec.NudityPromise<any>> = new Map();

	public static initClient(call: (platform: 'ios'|'android'|'virtual')=>NativeClientIns){
		// 设置原生端服务
		let client = call('android'); // call('ios') || call('android') || call('virtual');
		if(client){
			NativeClient.client = client;
			client.onReceiveMessage(NativeClient.receiveMessage);
		}
	}

	public static sendMessage(api: string, data?: any, wait?: boolean, ccdata?: any){
		api = AFFIX_TEMPLATE.replace('key', api);
		if(this.client){
			let struct: NCAPIStructure<any, any> = {api} as any;
			struct.request = NativeClient.createRequest(data ?? {});
			if(wait){
				struct.response = NativeClient.createResponse(api, struct.request.cc_token);
			}

			if(ccdata){
				NativeClient.ccdata_cache.set(struct.request.cc_token, ccdata);
			}

			NativeClient.formatRequest(struct.request);

			mtec.log.tag([
				['NATIVE', 'tan'],
				['SEND', 'skyblue']
			].map(el=>el[0]+':'+el[1]).join(';'), api, '\n~~~~~~~~~~\nREQEST:', struct.request, '\n~~~~~~~~~~\n');

			this.client.callStaticMethod(api, struct.request);

			return struct;
		}else{
			mtec.log.tag('没有原生端服务: tomato');
			return void 0;
		}
	}

	private static receiveMessage(api: string, response: Awaited<NCAPIStructure<any, any>['response']>){
		mtec.log.tag([
			['NATIVE', 'tan'],
			['BACK', 'lime']
		].map(el=>el[0]+':'+el[1]).join(';'), api, '\n~~~~~~~~~~\nRESULT:', response, '\n~~~~~~~~~~\n');

		NativeClient.parseResponse(response);
		mtec.array.remove(NativeClient.used_token, response.cc_token);

		if(NativeClient.ccdata_cache.has(response.cc_token)){
			response.cc_data = NativeClient.ccdata_cache.get(response.cc_token);
			NativeClient.ccdata_cache.delete(response.cc_token);
		}

		let sign = api + ':' + response.cc_token;
		if(NativeClient.response_map.has(sign)){
			NativeClient.response_map.get(sign).resolve(response);
			NativeClient.response_map.delete(sign);
		}
	}

	private static createRequest(data: any){
		let cc_token = mtec.string.randomToken(5, 36, t=>!NativeClient.used_token.includes(t));
		NativeClient.used_token.push(cc_token);

		return {cc_token, data};
	}

	private static createResponse(api: string, cc_token: string){
		let np = new mtec.NudityPromise<any>();
		NativeClient.response_map.set(api + ':' + cc_token, np);
		return np.promise;
	}

	private static formatRequest(request: any){
		let list = [];

		if(typeof request == 'object' && !Array.isArray(request)){
			list.unshift(request);
		}

		while(list.length>0){
			let item = list.pop();
			if(Array.isArray(item)){
				item.forEach(el=>{
					if(typeof el == 'object'){
						list.push(el);
					}
				})
			}else if(item){
				for(let [key, value] of Object.entries(item)){
					if(typeof value == 'object'){
						list.push(value);
					}

					Reflect.deleteProperty(item, key);
					Reflect.set(item, AFFIX_TEMPLATE.replace('key', key), value);
				}
			}
		}

		return request;
	}

	private static parseResponse(response: any){
		let list = [];

		if(typeof response == 'object' && !Array.isArray(response)){
			list.unshift(response);
		}

		while(list.length>0){
			let item = list.pop();
			if(Array.isArray(item)){
				item.forEach(el=>{
					if(typeof el == 'object'){
						list.push(el);
					}
				})
			}else if(item){
				for(let [key, value] of Object.entries(item)){
					if(typeof value == 'object'){
						list.push(value);
					}

					Reflect.deleteProperty(item, key);
					Reflect.set(item, key.replace(AFFIX_REGEXP, '$1'), value);
				}
			}
		}

		return response;
	}
}

export abstract class NativeClientIns {
	public abstract callStaticMethod(api: string, request: NCAPIStructure<any, any>['request']): void;
	public abstract onReceiveMessage(callback: (api: string, response: Awaited<NCAPIStructure<any, any>['response']>)=>void): void;
}

/** 请求结构 */
export interface NCAPIStructure<Body, Result> {
	/** 请求接口标识符 */
	api: string;
	/** 请求数据 */
	request: {
		/** 请求令牌 */
		cc_token: string;
		/** 请求数据 */
		data: Body;
	};
	/** 响应数据 */
	response: Promise<{
		/** 请求令牌 */
		cc_token: string;
		/** 响应数据 */
		data: Result;
		/** cocos数据 */
		cc_data?: any;
	}>;
}
